#include "render_engine.h"
#include "animation_manager.h"
#include "wonderland.h"
#include <Ogre.h>

/*
 * This initalizes ogre..
 */

using namespace Ogre;
	
RenderEngine::RenderEngine(){
	mRoot = 0;
}

RenderEngine::~RenderEngine(){
    if (mRoot)
        delete mRoot;
}

void RenderEngine::start(){
	// Create the scene
	createScene();

	// Turn off the gui layer
	input_listener_->toggleGuiLayer();

	// We're ready to start rendering frames...
}

int RenderEngine::renderOneFrame(){
	return mRoot->renderOneFrame();
}

/*
 * Sets up the application - returns false if the user chooses to abandon configuration.
 */
bool RenderEngine::initialize(void){
	try {
		mRoot = new Root(Wonderland::Locations::Resources() + "ogre-plugins.cfg", Wonderland::Locations::Resources() + "ogre-main.cfg", Wonderland::Locations::Resources() + "../wonderland.log");
	} catch(Exception& e) {
		Wonderland::Utility::fatalException("Ogre exception: " + e.getFullDescription());
	}

	setupResources();

	configure();

	chooseSceneManager();
	createCamera();
	createViewports();

	// Set default mipmap level (NB some APIs ignore this)
	TextureManager::getSingleton().setDefaultNumMipmaps(5);

	// Load resources
	loadResources();

	// Add the OIS listener
	createFrameListener();

	return true;
}

/*
 * Configures the application - returns false if the user chooses to abandon configuration. 
 */
bool RenderEngine::configure(){
	//mRoot->restoreConfig();

	if(mRoot->showConfigDialog()){
		mWindow = mRoot->initialise(true, "Wonderland viewer");

		// Ignored in most windowed modes
		mRoot->getRenderSystem()->setWaitForVerticalBlank(true);

		return true;
	}
	else{
		return false;
	}
}

/// Method which will define the source of resources (other than current folder)
void RenderEngine::setupResources(){
    // Load resource paths from config file
    ConfigFile cf;

	try {
		cf.load(Wonderland::Locations::Resources() + "resources.cfg");
	} catch(Exception& e) {
		Wonderland::Utility::fatalException("Ogre exception: " + e.getFullDescription());
	}

    // Go through all sections & settings in the file
    ConfigFile::SectionIterator seci = cf.getSectionIterator();

    std::string secName, typeName, archName;
    while (seci.hasMoreElements())
    {
        secName = seci.peekNextKey();
        ConfigFile::SettingsMultiMap *settings = seci.getNext();
        ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i)
        {
            typeName = i->first;
            archName = i->second;

            ResourceGroupManager::getSingleton().addResourceLocation(
                Wonderland::Locations::Resources() + archName, typeName, secName);
        }
    }
}

void RenderEngine::chooseSceneManager(void){
	scene_manager_ = mRoot->createSceneManager(ST_GENERIC, "ExampleSMInstance");
}

void RenderEngine::createCamera(void)
{
	mCamera = scene_manager_->createCamera("/Viewer/Camera");
	mCamera->setPosition(Vector3(0,0,-300));
    mCamera->lookAt(Vector3(0,0,0));
	mCamera->setNearClipDistance(1);
}

void RenderEngine::destroyScene(void){
}

void RenderEngine::createViewports(void){
	// Create one viewport, entire window
	Viewport* vp = mWindow->addViewport(mCamera);
	vp->setBackgroundColour(ColourValue(1,1,1));

	// Alter the camera aspect ratio to match the viewport
	mCamera->setAspectRatio(
		Real(vp->getActualWidth()) / Real(vp->getActualHeight())
	);
}

void RenderEngine::loadResources(void){
	ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void RenderEngine::createFrameListener(void){
	input_listener_ = new InputListener(mWindow, mCamera, scene_manager_);
	mRoot->addFrameListener(input_listener_);

	animation_manager_ = new Wonderland::AnimationManager(scene_manager_);
	mRoot->addFrameListener(animation_manager_);
}

void RenderEngine::createScene(void){
	// Ambient lighting
	scene_manager_->setAmbientLight(ColourValue(0.2, 0.2, 0.2));

	// Shadow mapping
	scene_manager_->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE);
	LiSPSMShadowCameraSetup* lispsm = new LiSPSMShadowCameraSetup();
	lispsm->setOptimalAdjustFactor(0.3);
	scene_manager_->setShadowCameraSetup(ShadowCameraSetupPtr(lispsm));
	scene_manager_->setShadowColour(ColourValue(0.5, 0.5, 0.5));
	scene_manager_->setShadowTextureSize(256);
	scene_manager_->setShadowFarDistance(20);

	// Fog
	scene_manager_->setFog(Ogre::FOG_LINEAR, ColourValue(1,1,1), 0.09, 15, 30);

	// create the sun 
	Light *l = scene_manager_->createLight("/world/sunlight");
	l->setType(Light::LT_DIRECTIONAL);
	Vector3 dir(-1, -1, 0.4);
	dir.normalise();
	l->setDirection(dir);
	l->setDiffuseColour(1.0, 1.0, 1.0);

	// create the ground plane
	Plane plane(Vector3::UNIT_Y, 0);
	MeshManager::getSingleton().createPlane(
		"/world/ground", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane, 100,100,10,10,true,1,5,5,Vector3::UNIT_Z
	);
	Entity *ent = scene_manager_->createEntity("GroundEntity", "/world/ground");
	ent->setMaterialName("wonderland/grid");
	ent->setCastShadows(false);
	scene_manager_->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
}
